function y=lte_pn(c_init,len)

% y=lte_pn(c_init,len)
%
% Implementation of the 3GPP LTE PN sequence generator in section 7.2 of 36.211.

% Copyright 2012 Evrytania LLC (http://www.evrytania.com)
%
% Written by James Peroulas <james@evrytania.com>
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

error(nargchk(2,2,nargin));
error(chk_param(c_init,'c_init','scalar','integer','real','>=',0,'<',2^31));
error(chk_param(len,'len','scalar','integer','real','>=',0));

% Initial values of the state machines
x1=zeros(1,31);
x1(1)=1;
x2=NaN(1,31);
for t=1:31
  x2(t)=bitget(c_init,t);
end

% Create the transition matrices to advance the state by 1600 iterations.
if (0)
  x1_p1_mat=zeros(31,31);
  for t=1:30
    x1_p1_mat(t,t+1)=1;
  end
  x1_p1_mat(31,1)=1;
  x1_p1_mat(31,4)=1;
  x2_p1_mat=zeros(31,31);
  for t=1:30
    x2_p1_mat(t,t+1)=1;
  end
  x2_p1_mat(31,1)=1;
  x2_p1_mat(31,2)=1;
  x2_p1_mat(31,3)=1;
  x2_p1_mat(31,4)=1;
  x1_p1600_mat=x1_p1_mat;
  x2_p1600_mat=x2_p1_mat;
  for t=1:1599
    x1_p1600_mat=mod(x1_p1600_mat*x1_p1_mat,2);
    x2_p1600_mat=mod(x2_p1600_mat*x2_p1_mat,2);
  end
  fprintf('x1:\n');
  for t=1:31
    fprintf('%i ',x1_p1600_mat(t,:));
    fprintf('\n');
  end
  fprintf('x2:\n');
  for t=1:31
    fprintf('%i ',x2_p1600_mat(t,:));
    fprintf('\n');
  end
else
  x1_p1600_mat=[ ...
    0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 0 ; ...
    0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 ; ...
    0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 ; ...
    0 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 0 ; ...
    0 0 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 ; ...
    0 0 0 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 1 ; ...
    1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 0 ; ...
    0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 0 ; ...
    0 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 0 ; ...
    0 0 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 0 ; ...
    0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 1 ; ...
    1 0 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 1 ; ...
    1 1 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 0 ; ...
    0 1 1 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 1 ; ...
    1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 0 ; ...
    0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 0 ; ...
    0 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 0 ; ...
    0 0 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 0 ; ...
    0 0 0 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 1 ; ...
    1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 0 ; ...
    0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 0 ; ...
    0 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 1 ; ...
    1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 0 ; ...
    0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 0 ; ...
    0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 ; ...
    1 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 1 ; ...
    1 1 0 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 1 ; ...
    1 1 1 1 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 1 ; ...
    1 1 1 0 1 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 0 ; ...
    0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 1 ; ...
    1 0 1 0 1 0 1 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 0 ; ...
  ];
  x2_p1600_mat=[ ...
    0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 ; ...
    0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 ; ...
    0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 ; ...
    0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 ; ...
    0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 ; ...
    0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 ; ...
    0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 ; ...
    0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 ; ...
    1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 ; ...
    0 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 ; ...
    0 0 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 ; ...
    1 1 1 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 ; ...
    1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 ; ...
    0 1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 ; ...
    0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 ; ...
    1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 ; ...
    0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 ; ...
    0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 ; ...
    0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 ; ...
    1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 ; ...
    0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 ; ...
    0 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 0 ; ...
    0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 1 ; ...
    1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 ; ...
    0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 0 ; ...
    0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 0 ; ...
    0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 0 ; ...
    0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 ; ...
    1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 ; ...
    1 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 1 ; ...
    1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 0 0 ; ...
  ];
end
x1=transpose(mod(x1_p1600_mat*transpose(x1),2));
x2=transpose(mod(x2_p1600_mat*transpose(x2),2));

y=NaN(1,len);
for t=1:len
  y(t)=mod(x1(1)+x2(1),2);
  % Advance state machines
  x1=[x1(2:end) mod(x1(1)+x1(4),2)];
  x2=[x2(2:end) mod(x2(1)+x2(2)+x2(3)+x2(4),2)];
end

